<html>
    <head>
        <title>使用方法</title>
    </head>
    <body>
        <script>
            /*
                        二、使用方法

                             1、原生 IDistributedCache接口
                                      asp.net core 定义了 IDistributedCache 接口来获取/设置缓存值。

                                      但是IDistributedCache有些缺点：

                                          ①、它适用于字节数组，而不是.net 对象。 因此，你需要 序列化/反序列化 你所需要缓存的对象。

                                          ②、它为所有缓存项只提供了同一个键池，所以：

                                                    你需要关心键以区分不同类型的对象。

                                                    你需要关系多租户系统中不同租户的缓存项。

                                      IDistributedCache 接口是定义在 "Microsoft.Extensions.Caching.Abstractions"包中的。
                                      这意味着它不仅可以用于asp.net core应用程序，还可以用于其他任何类型的.net 程序。

                                      有关详情，轻阅读 asp.net core 的分布式缓存文档:  https://learn.microsoft.com/en-us/aspnet/core/performance/caching/distributed?view=aspnetcore-9.0


                            2、IDistributedCache<TCacheItem> 接口

                                      ABP 在 Volo.Abp.Cache 包中定义了通用的 IDistributedCache<TCacheItem> 接口。 TCacheItem 是存储在缓存中的对象的类型。
                                       
                                      IDistributedCache<TCacheItem>解决了上面解释的困难；

                                          ①、它在内部序列化/反序列化缓存的对象。

                                                    它默认使用JSON序列化，但可以通过替换依赖注入系统中的IDistributedCacheSerializer服务来覆盖。
                                            
                                          ②、它会根据缓存中"存储的对象类型" 自动向缓存键"添加名称前缀"。

                                                    默认缓存名称是缓存项类的全名（如果你的缓存项类以CacheItem后缀结尾，则后缀将被删除）。

                                                    你可以使用缓存项类上的CacheName属性来设置缓存名称。

                                          ③、它会自动将当前租户id添加到缓存键，以区分不同租户的缓存项（如果你的应用程序是多租户）。
                                             
                                                   如果你想在多租户应用程序中的所有租户之间共享缓存对象，请在缓存项类上定义IgnoreMultiTenancy属性以禁用此功能。
                                                
                                          ④、允许为每个应用程序定义"全局缓存密钥前缀"，以便不同的应用程序可以在共享分布式缓存服务器中使用其隔离的密钥池。


                                          ⑤、它可以尽可能容忍错误并绕过缓存。
                                          
                                                    当你在缓存服务器上遇到临时问题时，这很有用。

                                          ⑥、它具有GetManyAsync和SetManyAsync等方法，可显着提高批处理操作的性能。


                                            eg：在缓存中存储书名和价格

                                                    namespace MyProject
                                                    {
                                                        // 需要缓存的类型
                                                        public class BookCacheItem 
                                                        {
                                                            public string Name { get; set; }

                                                            public float Price { get; set; }
                                                        }
                                                    }

                                                你可以注入并使用 IDistributedCache<BookCacheItem> 服务来获取/设置  BookCacheItem 对象：

                                                    using System;
                                                    using System.Threading.Tasks;
                                                    using Microsoft.Extensions.Caching.Distributed; // 微软分布式缓存命名空间（所有.net 应用程序都可以用）
                                                    using Volo.Abp.Caching; // 注意引入命名空间
                                                    using Volo.Abp.DependencyInjection;

                                                    namespace MyProject
                                                    {
                                                        public class BookService : ITransientDependency
                                                        {
                                                            private readonly IDistributedCache<BookCacheItem> _cache; // 注入类型化缓存服务（泛型服务）

                                                            public BookService(IDistributedCache<BookCacheItem> cache)
                                                            {
                                                                _cache = cache;
                                                            }

                                                            // 从分布式缓存中获取
                                                            public async Task<BookCacheItem> GetAsync(Guid bookId)
                                                            {
                                                                return await _cache.GetOrAddAsync(
                                                                    bookId.ToString(), // 缓存键
                                                                    async () => await GetBookFromDatabaseAsync(bookId), // 缓存值，如果缓存中没有，使用该值填充缓存，并返回当前方法的返回值作为最终值
                                                                    () => new DistributedCacheEntryOptions     // （可选参数）用于设置缓存项的一些缓存策略
                                                                    {
                                                                        AbsoluteExpiration = DateTimeOffset.Now.AddHours(1) // 缓存过期时间
                                                                    }
                                                                );
                                                            }

                                                            // 从数据库获取实体
                                                            private Task<BookCacheItem> GetBookFromDatabaseAsync(Guid bookId)
                                                            {
                                                                //TODO: get from database
                                                            }
                                                        }
                                                    }

                                                    此示例服务使用GetOrAddAsync()方法从缓存中获取图书项。
                                                    
                                                    GetOrAddAsync是ABP添加到标准ASP.NETCore 分布式缓存方法的扩展方法。

                                                    如果在缓存中未找到图书，则调用工厂方法（GetBookFromDatabaseAsync在本例中）从原始源检索图书项。

                                                    GetOrAddAsync可选地获取DistributedCacheEntryOptions，可用于设置缓存项的生命周期。


                                                    （支持与ASP.NETCore的标准相同的方法，因此可以参考它的文档。）



                             3、IDistributedCache<TCacheItem, TCacheKey>接口   

                                                    IDistributedCache<TCacheItem>接口中存储键类型只能是string（因此，如果你需要使用不同类型的缓存键，则需要手动将键转换为字符串）。

                                                    虽然这看似没什么大不了的，单当你的缓存键类型不是string时，可以使用 IDistributedCache<TCacheItem,TCacheKey>接口。

                                                           eg: 在缓存中存储书名和价格

                                                                    using Volo.Abp.Caching;
                                                                    namespace MyProject
                                                                    {
                                                                        [CacheName("Books")]  // 缓存名特性
                                                                        public class BookCacheItem
                                                                        {
                                                                            public string Name { get; set; }

                                                                            public float Price { get; set; }
                                                                        }
                                                                    }

                                                                此示例使用BookCacheItem类的 [CacheName]特性，来设置缓存名称。
                                                    
                                                                你可以注入并使用 IDistributedCache<BookCacheItem,Guid> 服务来获取/设置 BookCacheItem 对象：

                                                                    using System;
                                                                    using System.Threading.Tasks;
                                                                    using Microsoft.Extensions.Caching.Distributed;
                                                                    using Volo.Abp.Caching;
                                                                    using Volo.Abp.DependencyInjection;

                                                                    namespace MyProject
                                                                    {
                                                                        public class BookService : ITransientDependency
                                                                        {
                                                                            private readonly IDistributedCache<BookCacheItem, Guid> _cache;

                                                                            public BookService(IDistributedCache<BookCacheItem, Guid> cache)
                                                                            {
                                                                                _cache = cache;
                                                                            }

                                                                            public async Task<BookCacheItem> GetAsync(Guid bookId)
                                                                            {
                                                                                return await _cache.GetOrAddAsync(
                                                                                    bookId, // GUID类型的缓存键
                                                                                    async () => await GetBookFromDatabaseAsync(bookId), // 缓存中没有就从数据库查，查出来的只作为缓存值返回，并保存到缓存中
                                                                                    () => new DistributedCacheEntryOptions
                                                                                    {
                                                                                        AbsoluteExpiration = DateTimeOffset.Now.AddHours(1)
                                                                                    }
                                                                                );
                                                                            }
                                                                            private Task<BookCacheItem> GetBookFromDatabaseAsync(Guid bookId)
                                                                            {
                                                                                //TODO: get from database
                                                                            }
                                                                        }
                                                                    }

                                                            此示例服务使用GetOrAddAsync()方法从缓存中获取图书项目。GetOrAddAsync是ABP添加到标准ASP.NETCore分布式缓存方法的扩展方法。

                                                            由于缓存被显式实现为使用Guid作为缓存键，因此Guid值被传递给_cache_GetOrAddAsync()方法。


                                                            ★、复杂类型作为缓存键

                                                                    IDistributedCache<TCacheItem,TCacheKey> 内部使用 TCacheKey.ToSting() 方法将其转换为字符串。

                                                                    所以如果需要使用复杂对象作为缓存键，则需要覆盖类的ToString()方法。

                                                                    eg:  
                                                                        用作缓存键的示例类：

                                                                            public class UserInOrganizationCacheKey
                                                                            {
                                                                                public Guid UserId { get; set; }
                                                                                
                                                                                public Guid OrganizationId { get; set; }
                                                                                
                                                                                // 重写类的ToString方法
                                                                                public override string ToString()
                                                                                {
                                                                                    return $"{UserId}_{OrganizationId}";
                                                                                }
                                                                            }  

                                                                        示例用法：
                                                                            public class BookService : ITransientDependency
                                                                            {
                                                                                private readonly IDistributedCache<UserCacheItem, UserInOrganizationCacheKey> _cache;

                                                                                public BookService(IDistributedCache<UserCacheItem, UserInOrganizationCacheKey> cache)
                                                                                {
                                                                                    _cache = cache;
                                                                                }
                                                                            }
                    */
        </script>
    </body>
</html>